package bowshot

import (
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

// define
const (
	DefaultBufferSize  = 4096
	MaximumBufferSize  = 65536
	DefaultMaximumSize = int64(104857600)
	RejectMaximumSize  = int64(5 * 1024 * 1024 * 1024)
)

// applyBufferSize todo
func applyBufferSize(size, dsize int) int {
	if size > 0 && size < MaximumBufferSize {
		return size
	}
	return dsize
}

// Level is logger
type Level int32

const (
	// DebugLevel value
	DebugLevel Level = iota // value 0
	// InfoLevel value
	InfoLevel
	// ErrorLevel value
	ErrorLevel
	// FatalLevel value
	FatalLevel
)

// Slot todo
type Slot struct {
	level Level
	log   *slotWriter
	bus   *slotWriter
}

// SizeSettings todo
type SizeSettings struct {
	AccessBufferSize int
	LogBufferSize    int
	MaximumSize      int64
	Name             string
	Expires          int
}

// DefaultSizeSettings todo
func DefaultSizeSettings() *SizeSettings {
	return &SizeSettings{AccessBufferSize: 8192,
		LogBufferSize: 4096,
		MaximumSize:   DefaultMaximumSize,
		Name:          "Bowshot",
		Expires:       30} /// default please not use
}

// ParseMaximumSize todo
func ParseMaximumSize(s string) int64 {
	if len(s) == 0 {
		return DefaultBufferSize
	}
	if s[len(s)-1] == 'B' || s[len(s)-1] == 'b' {
		s = s[0 : len(s)-1]
	}
	if len(s) == 0 {
		return DefaultBufferSize
	}
	var radix int64 = 1
	sl := len(s) - 1
	switch s[len(s)-1] {
	case 'T', 't':
		radix = 1024 * 1024 * 1024 * 1024
	case 'G', 'g':
		radix = 1024 * 1024 * 1024
	case 'M', 'm':
		radix = 1024 * 1024
	case 'K', 'k':
		radix = 1024
	default:
		sl++
	}
	if sl < 1 {
		return DefaultMaximumSize
	}
	p, err := strconv.ParseInt(s[0:sl], 10, 64)
	if err != nil {
		return DefaultMaximumSize
	}
	return p * radix
}

// IsDebugEnable todo
func IsDebugEnable(name string) bool {
	ek := strings.ToUpper(name) + "_TRACE"
	ev := os.Getenv(ek)
	if len(ev) == 0 {
		return false
	}
	return ev == "1" || strings.ToUpper(ev) == "TRUE"
}

// InitializeEx todo
func (l *Slot) InitializeEx(af, ef string, ss *SizeSettings) error {
	if ss.MaximumSize <= 0 || ss.MaximumSize > RejectMaximumSize {
		ss.MaximumSize = DefaultMaximumSize
	}
	l.bus = newSlotWriter(af, applyBufferSize(ss.AccessBufferSize, 8192), ss.MaximumSize)
	l.log = newSlotWriter(ef, applyBufferSize(ss.LogBufferSize, 4096), ss.MaximumSize)
	if IsDebugEnable(ss.Name) {
		l.level = DebugLevel
	} else {
		l.level = InfoLevel
	}
	l.bus.expires = ss.Expires
	l.log.expires = ss.Expires
	return nil
}

// Initialize todo
func (l *Slot) Initialize(af, ef string) error {
	return l.InitializeEx(af, ef, DefaultSizeSettings())
}

// SetLevel log level
func (l *Slot) SetLevel(lv Level) {
	atomic.StoreInt32((*int32)(&l.level), int32(lv))
}

// Close all
func (l *Slot) Close() {
	if l.log != nil {
		l.log.close()
	}
	if l.bus != nil {
		l.bus.close()
	}
}

// Exit Slot hook exit
func (l *Slot) Exit(exitcode int) {
	l.Close()
	os.Exit(exitcode)
}
